import { BooleanProperty, geoDestination, NumberProperty, RayEditing } from "xbsj-xe2/dist-node/xe2-base-objects";
import { PartialWithUndefinedReactivePropsToNativeProps, ReactivePropsToNativePropsAndChanged, createNextAnimateFrameEvent, extendClassProps, react, reactArray, reactArrayWithUndefined, track } from "xbsj-xe2/dist-node/xe2-base-utils";
import { SceneObjectKey } from "xbsj-xe2/dist-node/xe2-utils";
import { ESGeoExtrudedPolygonUFMS } from "../ESGeoExtrudedPolygonUFMS";
import { ESGeoExtrudedPolygonUFMSFromCircle } from "../ESGeoExtrudedPolygonUFMSFromCircle";
import * as Cesium from 'cesium';
/**
 * 测试 ModelSer使用的接触网裁剪体
 */
export class ESOverheadLinesClippingUFMS extends ESGeoExtrudedPolygonUFMS {
    static override readonly type = this.register('ESOverheadLinesClippingUFMS', this, { chsName: 'ModelSer使用的地理多边形体', tags: ['ESObjects', '_ES_Impl_Cesium', '_ES_Impl_UE'], description: "地理多边形体" });
    override get typeName() { return 'ESOverheadLinesClippingUFMS'; }
    override get defaultProps() { return ESOverheadLinesClippingUFMS.createDefaultProps(); }
    override get json() { return this._innerGetJson() as JsonType; }
    override set json(value: JsonType) { this._innerSetJson(value); }

    public start = this.ad(new ESGeoExtrudedPolygonUFMSFromCircle());
    public end = this.ad(new ESGeoExtrudedPolygonUFMSFromCircle());
    public brand = this.ad(new ESGeoExtrudedPolygonUFMS());

    static override defaults = {
        ...ESGeoExtrudedPolygonUFMS.defaults,
        columnSize: 1,
        columnHeight: 3,
        brandLength: 2,
        brandHeight: 6,
        brandWidth: 2,
    }

    // 中间变量
    private _innerPositionReact = this.disposeVar(reactArrayWithUndefined<[number, number, number]>(undefined));
    private _innerRotationReact = this.disposeVar(reactArray<[number, number, number]>([0, 0, 0]));
    private _innerDistance = this.disposeVar(react<number>(0));

    // 监听editing变化，开启双点编辑
    private _rayEditing = this.disposeVar(new RayEditing(this._innerPositionReact, this._innerRotationReact, this._innerDistance, [this, 'positionEditing'], this.components));
    get rayEditing() { return this._rayEditing; }

    constructor(id?: SceneObjectKey) {
        super(id);
        this.collision = false;
        this.operation = '1'
        {
            // 互相监听，RayEditing输入的位置是undefined就会开启双点
            {
                const updated = () => {
                    if (!this.points || this.points.length == 0) {
                        this._innerPositionReact.value = undefined;
                    }
                }
                updated();
                this.dispose(this.pointsChanged.disposableOn(updated));
            }
            {
                const updated = () => {
                    if (this._innerPositionReact.value == undefined) {
                        this.points = [];
                    }
                }
                this.dispose(this._innerPositionReact.changed.disposableOn(updated));
            }
        }
        const start = this.start;
        const end = this.end;
        const brand = this.brand;
        start.perPositionHeight = end.perPositionHeight = brand.perPositionHeight = false;
        this.components.add(start);
        this.components.add(end);
        this.components.add(brand);
        this.ad(() => {
            this.components.delete(start);
            this.components.delete(end);
            this.components.delete(brand);
        })
        {
            this.ad(track([start, 'fillColor'], [this, 'fillColor']));
            this.ad(track([end, 'fillColor'], [this, 'fillColor']));
            this.ad(track([brand, 'fillColor'], [this, 'fillColor']));
        }
        {
            const update = () => {
                if (this.positionEditing) {
                    start.show = end.show = brand.show = false;
                    return;
                } else {
                    start.show = end.show = brand.show = this.show;
                    start.position = this._innerPositionReact.value;
                    end.position = getEnd(this._innerPositionReact.value, this._innerRotationReact.value, this._innerDistance.value);
                    start.height = end.height = this._innerPositionReact.value && this._innerPositionReact.value[2];
                    start.rotation = end.rotation = this._innerRotationReact.value;
                }
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(
                this.positionEditingChanged,
                this._innerPositionReact.changed,
                this._innerRotationReact.changed,
                this._innerDistance.changed,
                this.showChanged
            ))
            this.ad(event.don(update))
        }
        {
            const update = () => {
                start.radius = end.radius = Math.sqrt((this.columnSize ?? ESOverheadLinesClippingUFMS.defaults.columnSize) ** 2 / 2);
            }
            update()
            this.ad(this.columnSizeChanged.don(update));
        }
        {
            const update = () => {
                start.extrudedHeight = end.extrudedHeight = (start.height ?? 0) + (this.columnHeight ?? ESOverheadLinesClippingUFMS.defaults.columnHeight);
                brand.height = start.extrudedHeight - 0.5;
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(this.columnHeightChanged, start.heightChanged));
            this.ad(event.don(update));
        }
        {
            const update = () => {
                if (start.position && end.position) {
                    const radius = Math.sqrt((this.brandLength ?? ESOverheadLinesClippingUFMS.defaults.brandLength) ** 2 + ((this.brandWidth ?? ESOverheadLinesClippingUFMS.defaults.brandWidth) / 2) ** 2);
                    const radians = Math.acos((this.brandLength ?? ESOverheadLinesClippingUFMS.defaults.brandLength) / radius)
                    const beforePoints = this._getCircularCoordinates(start.position, radius, radians, [0, 3]);
                    const afterPoints = this._getCircularCoordinates(end.position, radius, radians, [1, 2])
                    if (beforePoints && afterPoints) {
                        brand.points = [...beforePoints, ...afterPoints.reverse(), beforePoints[0]];
                    } else {
                        brand.points = []
                    }
                }
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(this.brandLengthChanged, this.brandWidthChanged, start.positionChanged, end.positionChanged));
            this.ad(event.don(update));
        }
        {
            const update = () => {
                brand.extrudedHeight = (brand.height ?? 0) + (this.brandHeight ?? ESOverheadLinesClippingUFMS.defaults.brandHeight);
            }
            update();
            const event = this.ad(createNextAnimateFrameEvent(this.brandHeightChanged, brand.heightChanged));
            this.ad(event.don(update));
        }
    }
    override getESProperties() {
        const properties = { ...super.getESProperties() };
        return {
            ...properties,
            coordinate: [
                ...properties.coordinate,
                new NumberProperty('棱柱尺寸', '棱柱尺寸', true, false, [this, 'columnSize'], ESOverheadLinesClippingUFMS.defaults.columnSize),
                new NumberProperty('棱柱高度', '边数', true, false, [this, 'columnHeight'], ESOverheadLinesClippingUFMS.defaults.columnHeight),
                new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandLength'], ESOverheadLinesClippingUFMS.defaults.brandLength),
                new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandHeight'], ESOverheadLinesClippingUFMS.defaults.brandHeight),
                new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandWidth'], ESOverheadLinesClippingUFMS.defaults.brandWidth),
                new BooleanProperty('编辑位置', '编辑位置', true, false, [this, 'positionEditing'], false)
            ]
        };
    };
    override getProperties(language: string) {
        return [
            ...super.getProperties(language),
            new NumberProperty('棱柱尺寸', '棱柱尺寸', true, false, [this, 'columnSize'], ESOverheadLinesClippingUFMS.defaults.columnSize),
            new NumberProperty('棱柱高度', '边数', true, false, [this, 'columnHeight'], ESOverheadLinesClippingUFMS.defaults.columnHeight),
            new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandLength'], ESOverheadLinesClippingUFMS.defaults.brandLength),
            new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandHeight'], ESOverheadLinesClippingUFMS.defaults.brandHeight),
            new NumberProperty('棱柱高度', '边数', true, false, [this, 'brandWidth'], ESOverheadLinesClippingUFMS.defaults.brandWidth),
            new BooleanProperty('编辑位置', '编辑位置', true, false, [this, 'positionEditing'], false)
        ];
    }
    private _getCircularCoordinates(point: [number, number, number], radius: number, radians: number, contain: number[]) {
        // 都是在笛卡尔坐标下进行计算的
        const sceneObject = this;
        const sides = 4;
        const circlePoints = [] as [number, number, number][];
        if (radius <= 0 || sides <= 0) return sceneObject.points
        let Cartesian3_to_WGS84 = function (point: Cesium.Cartesian3): [number, number, number] {
            let cartesian33 = new Cesium.Cartesian3(point.x, point.y, point.z);
            let cartographic = Cesium.Cartographic.fromCartesian(cartesian33);
            let lat = Cesium.Math.toDegrees(cartographic.latitude);
            let lng = Cesium.Math.toDegrees(cartographic.longitude);
            let alt = cartographic.height;
            return [lng, lat, alt];
        }
        //法向量
        const normalVector = Cesium.Cartesian3.fromDegrees(...point);
        // 辅助向量
        const auxiliaryVectorZ = new Cesium.Cartesian3(0, 0, 1);
        const auxiliaryVectorX = new Cesium.Cartesian3(1, 0, 0);
        // 向量点乘结果为正负1就是平行
        const circleStartPoint = Cesium.Cartesian3.cross(
            normalVector,
            Math.abs(Cesium.Cartesian3.dot(normalVector, auxiliaryVectorZ)) != 1 ? auxiliaryVectorZ : auxiliaryVectorX,
            new Cesium.Cartesian3()
        );
        // 归一化，乘半径
        Cesium.Cartesian3.normalize(circleStartPoint, circleStartPoint);
        Cesium.Cartesian3.multiplyByScalar(circleStartPoint, radius, circleStartPoint);
        // 通过旋转，计算圆点结果
        for (let j = 0; j < sides; j++) {
            //获取旋转矩阵
            let quat = Cesium.Quaternion.fromAxisAngle(normalVector,
                j * 2 * Math.PI / sides + (j % 2 == 0 ? -1 : 1) * (Math.PI / 4 - radians) + Cesium.Math.toRadians(135 - sceneObject._innerRotationReact.value[0])
            );
            let rot_mat3 = Cesium.Matrix3.fromQuaternion(quat);
            let m = Cesium.Matrix4.fromRotationTranslation(rot_mat3, Cesium.Cartesian3.ZERO);
            Cesium.Matrix4.multiplyByTranslation(m, circleStartPoint, m);
            const startPoint = Cesium.Matrix4.getTranslation(m, new Cesium.Cartesian3());
            Cesium.Cartesian3.add(Cesium.Cartesian3.fromDegrees(...point), startPoint, startPoint);
            if (contain == undefined || contain.includes(j))
                circlePoints.push(
                    Cartesian3_to_WGS84(startPoint),
                )

        }
        return circlePoints;
    }
}

export namespace ESOverheadLinesClippingUFMS {
    export const createDefaultProps = () => ({
        ...ESGeoExtrudedPolygonUFMS.createDefaultProps(),
        columnSize: undefined as number | undefined,
        columnHeight: undefined as number | undefined,
        brandLength: undefined as number | undefined,
        brandHeight: undefined as number | undefined,
        brandWidth: undefined as number | undefined,
        positionEditing: false,
    });
}
extendClassProps(ESOverheadLinesClippingUFMS.prototype, ESOverheadLinesClippingUFMS.createDefaultProps);
export interface ESOverheadLinesClippingUFMS extends ReactivePropsToNativePropsAndChanged<ReturnType<typeof ESOverheadLinesClippingUFMS.createDefaultProps>> { }
type JsonType = PartialWithUndefinedReactivePropsToNativeProps<ReturnType<typeof ESOverheadLinesClippingUFMS.createDefaultProps> & { type: string }>;
export function getEnd(position: [number, number, number] | undefined, rotation: [number, number, number], distance: number) {
    if (!position) return;
    const groundDistance = distance * Math.cos(rotation[1] * Math.PI / 180);
    const des = geoDestination(position, groundDistance, rotation[0]);
    if (!des) return;
    const height = distance * Math.sin(rotation[1] * Math.PI / 180);
    des[2] += height;
    return des;
}